﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Text;
using System.IO;
using System.Xml;
using System.Windows.Forms;
using System.Drawing;

using Elegant.Ui;
using iPdf.Properties;

namespace iPdf
{
    class TataFile
    {
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields")]
        public string FileName;
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields")]
        public int Index;

        protected string IndexString
        {
            get
            {
                string indexStr = Helper.GetChinaStyleIndexNo(Index);
                if (indexStr == string.Empty)
                {
                    indexStr = "      ";
                }
                else
                {
                    indexStr = Helper.Ampersand + indexStr + Helper.Space;
                }

                return indexStr;
            }
        }

        public virtual bool IsValid
        {
            get
            {
                return File.Exists(FileName);
            }
        }

        public FileFormat Format
        {
            get
            {
                return new FileFormat(FileName);
            }
        }

        public TataFile()
        {
            FileName = string.Empty;
        }

        public TataFile(string fileName)
        {
            FileName = fileName;
        }

        public override string ToString()
        {
            return IndexString + Path.GetFileNameWithoutExtension(FileName) + 
                    Path.GetExtension(FileName);
        }

        public override bool Equals(object obj)
        {
            if (obj is string)
            {
                return Equals((string)obj);
            }

            if (obj is TataFile)
            {
                return Equals((TataFile)obj);
            }

            return false;
        }

        public bool Equals(string fileName)
        {
            return FileName.Equals(fileName, StringComparison.OrdinalIgnoreCase);
        }

        public bool Equals(TataFile rhs)
        {
            return Equals(rhs.FileName);
        }

        public override int GetHashCode()
        {
            return FileName.GetHashCode();
        }

        public virtual void Click()
        {
            Global.MainWindow.AddItemsToGrid(new string[] { FileName });
        }
    }

    class TataSession : TataFile
    {
        #region Fields
        private static string sessionsDir = Helper.RoamingDir + "Sessions\\";
        private static string SessionTag = "Session";
        private static string PageAttr = "Page";
        private static string DescriptionAttr = "Description";

        private string description;
        public const int MaxSessionsCount = 100;
        private TataRibbonPage sessionPage;

        #endregion

        #region Properties

        public static bool HasSessions
        {
            get
            {
                return Directory.GetFiles(sessionsDir, Wildcard, 
                    SearchOption.AllDirectories).Length > 0;
            }
        }

        public static string SessionsDir
        {
            get
            {
                if (!Directory.Exists(sessionsDir))
                {
                    Directory.CreateDirectory(sessionsDir);
                }
                return sessionsDir;
            }
        }

        public TataRibbonPage Page
        {
            get
            {
                if (sessionPage == null)
                {
                    XmlElement sessionNode = ExtractSessionNode(FileName);
                    if (sessionNode == null)
                    {
                        sessionPage = null;
                    }
                    else
                    {
                        sessionPage = Global.MainWindow.
                            GetPageByName(sessionNode.GetAttribute(PageAttr));
                    }
                }

                return sessionPage;
            }
        }

        public bool ImportingEnabled
        {
            get
            {
                return !Global.MainWindow.Busy && !ItemRetrievor.Busy;
            }
        }

        public string Description
        {
            get
            {
                if (description == null)
                {
                    if (!IsValid)
                    {
                        return string.Empty;
                    }

                    string decryptedFileName = 
                        FileCryptography.DecryptFile(FileName, Global.SessionPassword);

                    XmlDocument xmlDoc = new XmlDocument();
                    xmlDoc.Load(decryptedFileName);

                    XmlNodeList sessionNodes = xmlDoc.GetElementsByTagName("Session");

                    if (sessionNodes == null || sessionNodes.Count == 0)
                    {
                        return string.Empty;
                    }

                    XmlElement sessionNode = (XmlElement)sessionNodes[0];
                    description = sessionNode.GetAttribute("Description");
                    Helper.DeleteFile(decryptedFileName);
                }

                return description;
            }
            set
            {
                if (string.IsNullOrEmpty(value))
                {
                    return;
                }

                description = value;

                string decryptedFileName = FileCryptography.DecryptFile(FileName, Global.SessionPassword);

                XmlDocument xmlDoc = new XmlDocument();
                xmlDoc.Load(decryptedFileName);

                XmlNodeList sessionNodes = xmlDoc.GetElementsByTagName("Session");

                if (sessionNodes == null || sessionNodes.Count == 0)
                {
                    throw new InvalidDataException("Invalid session");
                }

                XmlElement sessionNode = (XmlElement)sessionNodes[0];
                sessionNode.SetAttribute("Description", description);

                xmlDoc.Save(FileName);

                string encryptedFileName = FileCryptography.EncryptFile(FileName, Global.SessionPassword);
                Helper.MoveFile(encryptedFileName, FileName);

                Helper.DeleteFile(decryptedFileName);
            }
        }

        public string Name
        {
            get
            {
                return Path.GetFileNameWithoutExtension(FileName);
            }
        }

        public static FileFormat SessionFormat
        {
            get
            {
                return FileFormat.XmlFormat;
            }
        }

        public static string Wildcard
        {
            get
            {
                return SessionFormat.Wildcards[0];
            }
        }

        public static string Extension
        {
            get
            {
                return SessionFormat.Extensions[0];
            }
        }

        #endregion

        #region Ctor
        public TataSession()
        {
            if (!Directory.Exists(sessionsDir))
            {
                Directory.CreateDirectory(sessionsDir);
            }
            FileName = string.Empty;
        }

        public TataSession(string sessionName)
            : this()
        {
            FileName = sessionsDir + sessionName + Global.XmlExt;
        }

        #endregion

        #region Methods

        #region Overrides
        public override string ToString()
        {
            return IndexString + Name;
        }

        public override void Click()
        {
            Import();
        }

        #endregion

        #region Operations

        public void Delete()
        {
            Helper.DeleteFile(FileName);
        }

        public void Rename(string newName, string newDescription)
        {
            if(IsValid)
            {
                string newFileName =  sessionsDir + newName + Global.XmlExt;
                File.Move(FileName, newFileName);
                FileName = newFileName;
                Description = newDescription;
            }
        }

        #endregion 

        #region Session import/export
        public static XmlElement ExtractSessionNode(string sessionFileName)
        {
            string decryptedFileName = FileCryptography.DecryptFile(sessionFileName, Global.SessionPassword);
            XmlDocument xmlDoc = new XmlDocument();
            xmlDoc.Load(decryptedFileName);

            XmlNodeList sessionsNodes = xmlDoc.GetElementsByTagName(SessionTag);

            if (sessionsNodes == null || sessionsNodes.Count == 0)
            {
                return null;
            }

            Helper.DeleteFile(decryptedFileName);
            return (XmlElement)sessionsNodes[0];
        }

        public void Import()
        {
            Import(this);
        }

        public static void Import(TataSession session)
        {
            if (session.ImportingEnabled)
            {
                Import(session.FileName);
            }
        }

        public static void Import(string sessionFileName)
        {
            XmlElement sessionNode = TataSession.ExtractSessionNode(sessionFileName);

            if (sessionNode == null)
            {
                throw new InvalidDataException("Invalid session file");
            }

            string pageName = sessionNode.GetAttribute(PageAttr);
            if (string.IsNullOrEmpty(pageName))
            {
                return;
            }

            TataRibbonPage page = Global.MainWindow.GetPageByName(pageName);
            Global.CurrentPage.ImportSession(sessionNode);
        }

        public static void ExportSession(string sessionFileName, string description, 
            XmlDocument xmlDoc)
        {
            xmlDoc.Save(sessionFileName);
            string encryptedFileName = FileCryptography.EncryptFile(sessionFileName, Global.SessionPassword);
            Helper.MoveFile(encryptedFileName, sessionFileName);

            TataSession session = new TataSession();
            session.FileName = sessionFileName;
            Global.MainWindow.Sessions.AddSession(session);
        }

        public static void ExportSession(string sessionFileName, string description)
        {
            XmlDocument xmlDoc;
            XmlElement sessionNode = CreateSessionNode(out xmlDoc);

            if (!string.IsNullOrEmpty(description))
            {
                sessionNode.SetAttribute(DescriptionAttr, description);
            }

            ExportSession(sessionFileName, description, xmlDoc);
        }

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "0#")]
        public static XmlElement CreateSessionNode(out XmlDocument xmlDoc)
        {
            xmlDoc = Helper.CreateXmlDocument();
            XmlElement sessionNode = xmlDoc.CreateElement(SessionTag);
            sessionNode.SetAttribute(PageAttr, Global.CurrentPage.Name);
            xmlDoc.AppendChild(sessionNode);

            Global.CurrentPage.ExportSession(sessionNode);

            return sessionNode;
        }

        public static bool EqualSessionNode(XmlElement sessionNode1, XmlElement sessionNode2)
        {
            sessionNode1.RemoveAttribute(DescriptionAttr);
            sessionNode2.RemoveAttribute(DescriptionAttr);

            return sessionNode1.OuterXml == sessionNode2.OuterXml;
        }

        #endregion

        #endregion
    }

    class RecentFiles
    {
        #region Fields
        bool loaded = false;
        RecentDocumentsControl control;
        Collection<TataFile> files = new Collection<TataFile>();
        private FileFormat[] supportedFormats;
        private FileFormat format;
        private string recentDocumentsPath = Helper.RoamingDir + "RecentDocuments.xml";

        int maxVisibleRecentFilesCount = 15;

        Dictionary<FileFormat, int> formatCountTable = new Dictionary<FileFormat, int>();

        protected const string recentButtonTypeName = "RecentDocumentsControlItemButton";

        #endregion

        #region Properties

        public RecentDocumentsControl Control
        {
            get
            {
                return control;
            }
        }

        public FileFormat Format
        {
            get
            {
                return format;
            }
            set
            {
                if (value == null)
                {
                    return;
                }

                format = value;
            }
        }


        public Collection<TataFile> Files
        {
            get
            {
                return files;
            }
        }

        public bool Enabled
        {
            get
            {
                return Control.Enabled;
            }
            set
            {
                Control.Enabled = value;
            }
        }

        protected System.Windows.Forms.Control ItemsContainer
        {
            get
            {
                foreach(System.Windows.Forms.Control control in this.control.Controls)
                {
                    if (control is IContainerControl)
                    {
                        return control;
                    }
                }

                return null;
            }
        }

        private List<Elegant.Ui.Button> PopupItems
        {
            get
            {
                if (control == null)
                {
                    return null;
                }
                List<Elegant.Ui.Button> items = new List<Elegant.Ui.Button>();

                if (ItemsContainer == null)
                {
                    return null;
                }

                foreach (Elegant.Ui.Control item in ItemsContainer.Controls)
                {
                    Type type = item.GetType();
                    if (type.Name == recentButtonTypeName)
                    {
                        items.Add((Elegant.Ui.Button)item);
                    }
                }

                return items;
            }
        }

        protected ListWithEvents<object> ControlItems
        {
            get
            {
                return control.Items;
            }
        }

        #endregion

        #region Ctor
        public RecentFiles()
        {
            control = new RecentDocumentsControl();
            control.ItemClick += new EventHandler<RecentDocumentsControlItemClickEventArgs>
                                                    (RecentDocumentsItem_Click);

            supportedFormats = new FileFormat[]
                                                {
                                                    FileFormat.PdfFormat,
                                                    FileFormat.WordFormat,
                                                    FileFormat.ExcelFormat,
                                                    FileFormat.PowerPointFormat,
                                                    TataSession.SessionFormat
                                                };
        }
        #endregion

        #region Methods

        public void ClearControl()
        {
            this.control.Items.Clear();
        }

        /// <summary>
        /// set up screen tips for the items in the control
        /// </summary>
        protected void SetupScreenTip()
        {
            foreach (Elegant.Ui.Button button in PopupItems)
            {
                TataFile file = (TataFile)button.Tag;
                button.ScreenTip.Text = file.FileName;
            }
        }

        protected int GetPreviousItemIndex(int index)
        {
            for (int i = index - 1; i >= 0; --i)
            {
                if (format.IsOneOf(files[i].FileName))
                {
                    return i;
                }
            }

            return -1;
        }

        public void Add(TataFile file)
        {
            if (file.IsValid == false)
            {
                Remove(file);
                return;
            }

            if (files.Contains(file))
            {
                Remove(file);
            }
            files.Insert(0, file);
            RefreshCountTable(file, true);
        }

        public void Add(string fileName)
        {
            if (string.IsNullOrEmpty(fileName) || !File.Exists(fileName))
            {
                return;
            }

            TataFile file = new TataFile(fileName);
            Add(file);
        }

        public void Remove(TataFile file)
        {
            if (files.Contains(file))
            {
                files.Remove(file);
                RefreshCountTable(file, false);
            }
        }

        public void Remove(string fileName)
        {
            Remove(new TataFile(fileName));
        }

        protected void Clear()
        {
            if (ControlItems.Count > 0)
            {
                ControlItems.Clear();
            }
        }

        private void RecentDocumentsItem_Click(object sender, RecentDocumentsControlItemClickEventArgs e)
        {
            Clear();
            TataFile file = (TataFile)e.Item;
            if (file.IsValid)
            {
                file.Click();
            }
            else
            {
                //remove it from the recent files list
                Remove(file);
                Global.ShowNotification("The file " + Helper.QuoteString(file.ToString() ) +
                        " doesn't exist any more and thus it's been removed.");
            }
        }

        public Collection<TataFile> QueryByFormat(FileFormat format, int maxItemsCount)
        {
            Collection<TataFile> results = new Collection<TataFile>();
            foreach (TataFile item in Files)
            {
                if (item.IsValid && format.IsOneOf(item.FileName) )
                {
                    results.Add(item);
                }

                if (results.Count >= maxItemsCount)
                {
                    break;
                }
            }

            return results;
        }

        
        /// <summary>
        /// Refresh count table
        /// </summary>
        public void RefreshCountTable(TataFile file, bool add)
        {
            FileFormat format = file.Format;
            if (add)
            {
                if (!formatCountTable.ContainsKey(format))
                {
                    formatCountTable.Add(format, 0);
                }
                int count = formatCountTable[format];
                formatCountTable[format] = ++count;
                if(count > maxVisibleRecentFilesCount)
                {
                    //remove the last item
                    for (int i = files.Count - 1; i >= 0; i--)
                    {
                        if (files[i].Format == format)
                        {
                            Remove(files[i]);
                            return;
                        }
                    }
                }
            }
            else
            {
                if (formatCountTable.ContainsKey(format) &&
                        formatCountTable[format] > 0)
                {
                    formatCountTable[format]--;
                }
            }
        }

        public void Update()
        {
            Load();
            Clear();
            if (Global.MainWindow.Busy)
            {
                return;
            }

            List<TataFile> invalidFiles = new List<TataFile>();
            int index = 1;
            foreach (TataFile file in files)
            {
                if (file.IsValid && format.IsOneOf(file.FileName))
                {
                    file.Index = index;
                    ++index;
                    ControlItems.Add(file);
                }

                if (!file.IsValid)
                {
                    invalidFiles.Add(file);
                }

                if (ControlItems.Count == maxVisibleRecentFilesCount)
                {
                    break;
                }
            }

            foreach (TataFile file in invalidFiles)
            {
                Remove(file);
            }

            SetupScreenTip();
        }

        /// <summary>
        /// Save the recent documents list to the RecentDocuments.xml
        /// </summary>
        public void Save()
        {
            XmlDocument xmlDoc = Helper.CreateXmlDocument();
            XmlElement root = xmlDoc.CreateElement("Tata");
            xmlDoc.AppendChild(root);
            XmlElement recentDocEle = xmlDoc.CreateElement("RecentDocuments");
            root.AppendChild(recentDocEle);
            foreach (TataFile file in files)
            {
                if (TataSession.SessionFormat.IsOneOf(file.FileName))
                {
                    //we don't save session cause sessions are processed separately
                    continue;
                }

                XmlElement itemEle = xmlDoc.CreateElement("Item");
                itemEle.InnerText = file.FileName;
                recentDocEle.AppendChild(itemEle);
            }

            xmlDoc.Save(recentDocumentsPath);
        }

        /// <summary>
        /// Load the recent documents list from the RecentDocuments.xml
        /// </summary>
        public void Load()
        {
            if (!loaded && File.Exists(recentDocumentsPath))
            {
                XmlDocument xmlDoc = new XmlDocument();
                xmlDoc.Load(recentDocumentsPath);
                XmlNodeList nodeList = xmlDoc.GetElementsByTagName("Item");
                foreach (XmlNode node in nodeList)
                {
                    TataFile file = new TataFile(node.InnerText);
                    if (file.IsValid && !files.Contains(file))
                    {
                        files.Add(file);
                        RefreshCountTable(file, true);
                    }
                }
            }

            loaded = true;
        }

        public void Click(int index)
        {
            if (index >= 0 && index < ControlItems.Count)
            {
                RecentDocumentsControlItemClickEventArgs args =
                    new RecentDocumentsControlItemClickEventArgs(ControlItems[index]);
                RecentDocumentsItem_Click(control, args);
                Global.RibbonMenu.Hide();
            }
        }

        public void ProcessKey(Keys key)
        {
            Click(key - Keys.D1);
            Click(key - Keys.NumPad1);
        }


        #endregion
    }
}
